package com.turing.data.service.element.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.turing.common.entity.Context;
import com.turing.common.entity.UserToken;
import com.turing.common.response.CommonCode;
import com.turing.common.response.PageBean;
import com.turing.common.response.ResponseVO;
import com.turing.common.util.*;
import com.turing.data.dao.element.ElementMapper;
import com.turing.data.dao.element.ElementPackageMapper;
import com.turing.data.dao.element.TemplateMapper;
import com.turing.data.inner.TaskRecordInnerService;
import com.turing.data.inner.params.out.ElementDetailVO;
import com.turing.data.model.domain.element.Element;
import com.turing.data.model.domain.element.ElementGroup;
import com.turing.data.model.domain.element.SubProduct;
import com.turing.data.model.domain.element.Template;
import com.turing.data.model.dto.element.ElementPackage;
import com.turing.data.response.DrawResultCode;
import com.turing.data.service.element.ElementGroupService;
import com.turing.data.service.element.ElementService;
import com.turing.data.service.element.TemplateService;
import com.turing.data.vo.in.element.*;
import com.turing.data.vo.out.element.ElementPageBean;
import com.turing.data.vo.out.review.TaskElementVO;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestParam;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class ElementServiceImpl extends ServiceImpl<ElementMapper,Element> implements ElementService {

    @Autowired
    private ElementMapper elementMapper;

    @Autowired
    private TemplateService templateService;

    @Autowired
    private TemplateMapper templateMapper;

    @Autowired
    private ElementPackageMapper elementPackageMapper;

    @Autowired
    private ElementGroupService elementGroupService;

    /**
     * 事务对象
     */
    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private TaskRecordInnerService taskRecordInnerService;

    @Override
    public Element getElementByNameAndTemplate(String eleName, String templateSn) {
        return new LambdaQueryChainWrapper<>(elementMapper)
            .eq(Element::getTemplateId, templateSn)
            .eq(Element::getName, eleName)
            .eq(Element::getIsDel, 0)
            .last("limit 1")
            .one();
    }

    @Override
    public List<Element> getElementsByTemplateSn(String templateSn,Integer category) {
        return new LambdaQueryChainWrapper<>(elementMapper)
            .eq(Element::getTemplateId, templateSn)
            .eq(Element::getIsDel, 0)
            .eq(Element::getCategory,category)
            .select(Element::getName,Element::getId)
            .list();
    }

    @Override
    public Element getElementById(Integer eleId) {
        return new LambdaQueryChainWrapper<>(elementMapper)
            .eq(Element::getId, eleId)
            .eq(Element::getIsDel, 0)
            .last("limit 1")
            .one();
    }

    @Override
    public List<String> getElementNamesByTemplateId(String templateSn) {
        return new LambdaQueryChainWrapper<>(elementMapper)
            .eq(Element::getTemplateId, templateSn)
            .eq(Element::getIsDel, 0)
            .select(Element::getName)
            .list().stream()
            .map(Element::getName)
            .collect(Collectors.toList());
    }

    @Override public PageBean<TemplateVo> getTemplateByPage(SearchVO searchVO){

        QueryWrapper<Template> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByAsc("privilege");
        queryWrapper.orderByDesc("created_time");
        queryWrapper.eq("is_del",0);

        //        or.andIsDelEqualTo( 0 );

        if (!StringUtils.isEmpty(searchVO.getSearchText())) {
            queryWrapper.like("template_name",searchVO.getSearchText());
        }
        if (Tools.isNotEmpty(searchVO.getType()) && !"0".equals(searchVO.getType())) {
            queryWrapper.eq("templateId",searchVO.getType());
        }

        try {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            if (!StringUtils.isEmpty(searchVO.getDateBegin())) {
                queryWrapper.ge("created_time",simpleDateFormat.parse(searchVO.getDateBegin() + " 00:00:00"));
            }

            if (!StringUtils.isEmpty(searchVO.getDateEnd())) {
                queryWrapper.le("created_time",simpleDateFormat.parse(searchVO.getDateEnd() + " 23:59:59"));
            }

        } catch (Exception e) {
            Log.error(e);
        }

        //权限
        List<String> privileges = new ArrayList<>(templateService.getPrivilegeTemplate());
        if (Tools.isEmpty(privileges)) {
            return new PageBean<>(searchVO.getPageIndex(),searchVO.getPageSize(),0);
        }
        queryWrapper.eq("template_sn",privileges);

        PageHelper.startPage(searchVO.getPageIndex(),searchVO.getPageSize());
        PageInfo<Template> pageInfo = new PageInfo<>(templateMapper.selectList(queryWrapper));
        //获取用户登录账号
        String userCode = Tools.isNotEmpty(Context.getTokenContext()) ? Context.getTokenContext().getUserCode() : "";
        int roleId = Tools.isNotEmpty(Context.getTokenContext()) ? Context.getTokenContext().getRoleId() : 0;
        List<TemplateVo> templateVos = pageInfo.getList().stream()
            .map(template -> {
                //权限控制是否可以开启自动审核
                return TemplateVo.builder()
                    .templateSn(template.getTemplateSn()).templateName(template.getTemplateName())
                    .templateGroup(template.getTemplateGroup())
                    .autoAudit(template.getAutoAudit())
                    .createdBy(template.getCreatedBy()).createdTime(template.getCreatedTime())
                    .typeId("0".equals(template.getType()) ? "0" : template.getType())
                    .typeName("0".equals(template.getType()) ? template.getTemplateName() : getTemplateNameById(template.getType()))
                    .build();
            })
            .collect(Collectors.toList());
        PageBean<TemplateVo> pageBean = new PageBean<>(searchVO.getPageIndex(),searchVO.getPageSize(),(int)pageInfo.getTotal());
        pageBean.setItems(templateVos);

        return pageBean;
    }

    @Override public String getTemplateNameById(String templateSn){
        Template template = templateMapper.selectById(templateSn);
        return Optional.ofNullable(template).map(Template::getTemplateName).orElse("");
    }

    @Override public ElementDetailVO getElementDetailById(Integer eleSn){
        Element element = elementMapper.selectById(eleSn);

        if (element != null) {
            ElementDetailVO detail = new ElementDetailVO();
            detail.setEleName(element.getName());
            detail.setEleSn(element.getId());
            detail.setEleType(element.getType());
            detail.setTemplateId(element.getTemplateId());
            detail.setMapName(element.getMapName());
            detail.setEleCategoryCh(element.getType());
            detail.setCounted(element.getCounted());
            return detail;
        }
        return null;
    }

    @Override public PageBean<ElementDetailVO> getElementList(ElementSearchVO searchVO){

        List<ElementDetailVO> elementDetailVOS = new ArrayList<>();
        PageHelper.startPage(searchVO.getPageIndex(),searchVO.getPageSize());

        List<Element> elements = new LambdaQueryChainWrapper<>(elementMapper)
                .eq(Element::getTemplateId, searchVO.getTemplateId())
                .eq(Element::getIsDel, 0).list();

        if (elements != null && elements.size() > 0) {
            for (Element element : elements) {
                ElementDetailVO elementDetailVO = new ElementDetailVO(element);
                elementDetailVOS.add(elementDetailVO);
            }
        }

        long l = new LambdaQueryChainWrapper<>(elementMapper)
                .eq(Element::getTemplateId, searchVO.getTemplateId())
                .eq(Element::getIsDel, 0).count();

        PageBean<ElementDetailVO> pageBean = new PageBean<>(searchVO.getPageIndex(),searchVO.getPageSize(),(int)l);
        pageBean.setItems(elementDetailVOS);

        return pageBean;
    }

    @Override public JSONArray getElementListWithTemplateSn(String templateSn){

        JSONArray resultArr = new JSONArray();

        String templateName = getTemplateNameById(templateSn);

        ElementSearchVO searchVO = new ElementSearchVO();
        searchVO.setTemplateId(templateSn);
        searchVO.setPageIndex(0);
        searchVO.setPageSize(9999);
        PageBean<ElementDetailVO> elementList = getElementList(searchVO);
        List<ElementDetailVO> items = elementList.getItems();
        if (items != null && items.size() > 0) {
            for (ElementDetailVO item : items) {
                if (item.isSelected()) {
                    JSONObject obj = new JSONObject();
                    obj.put("elemName",item.getEleName());
                    obj.put("elemId",item.getEleSn());
                    obj.put("elemCategory",templateName);
                    obj.put("multiple",item.getEleType().equals("单项") ? 1 : 2);
                    resultArr.add(obj);
                }
            }
        }

        return resultArr;
    }

    @Override public Integer updateTemplateElement(ElementDetailVO update){
        if (update == null) {
            return null;
        }
        if (update.getEleSn() == null || update.getEleSn() <= 0) {
            return null;
        }
        if (StringUtils.isEmpty(update.getEleType())
                || (!update.getEleType().equals("单项") && !update.getEleType().equals("多项"))) {
            return null;
        }

        Element eleUpdate = new Element();
        eleUpdate.setId(update.getEleSn());
        eleUpdate.setType(update.getEleType());
        eleUpdate.setUpdatedTime(new Date());
        return elementMapper.updateById(eleUpdate);
    }

    @Override public Integer removeTemplate(String templateSns){

        String[] split = templateSns.split(",");

        for (String s : split) {
            Template template = templateMapper.selectById(s);
            if (template == null) {
                return 0;
            }
            AssertUtil.notEquals(template.getCreatedBy(),Context.getTokenContext().getUserCode(),"无权限删除此场景");
            if (template.getType().equals("0")) {
                return -1;
            }
        }

        for (String s : split) {
            Template update = new Template();
            update.setTemplateSn(s);
            update.setIsDel(1);
            update.setUpdatedTime(new Date());
            templateMapper.updateById(update);
            //删除相关任务
            taskRecordInnerService.deleteProductChildTask(s);
        }

        return 1;
    }

    @Override public ResponseVO<Object> insertTemplate(TemplateInsertVO insertVO){
        //场景名称校验
        AssertUtil.isNotEmpty(getTemplateByName(insertVO.getTemplateName()),DrawResultCode.TEMPLATE_DUPLICATED);

        Template templateById = templateService.getById("11");
        Boolean result = transactionTemplate.execute(transactionStatus -> {
            // 插入场景
            Template template = Template.builder().templateSn(UUID.randomUUID().toString())
                    .templateName(insertVO.getTemplateName()).type(insertVO.getTypeId())
                    .createdTime(new Date()).createdBy(Context.getTokenContext().getUserCode())
                    .createdByName(Context.getTokenContext().getUserName()).needsJson(templateById.getNeedsJson())
                    .isDel(0).privilege(1).autoAudit(0)
                    .build();

            templateMapper.insert(template);

            //批量插入要素值
            List<Element> elements = insertVO.getElements().stream().map(element -> Element.builder().name(element.getEleName())
                    .type(element.getEleType()).packageSn(element.getGroupId()).parentEleId(element.getEleSn())
                    .templateId(template.getTemplateSn()).selected(element.isSelected() ? 1 : 0)
                    .createdTime(new Date())
                    .counted(element.getCounted()).isDel(0)
                    .build()).collect(Collectors.toList());
            return this.saveBatch(elements);
        });

        return result != null && result ? ResponseUtil.success() : ResponseUtil.create(CommonCode.ADD_FAIL);
    }

    @Override public ResponseVO<Object> updateTemplate(TemplateInsertVO insertVO){
        Template templateById = templateMapper.selectById(insertVO.getTemplateId());
        //场景名称校验
        AssertUtil.isEmpty(templateById,"需要修改的场景不存在");
        AssertUtil.notEquals(Context.getTokenContext().getUserCode(),templateById.getCreatedBy(),"权限不足");
        AssertUtil.isTrue(!Tools.equals(templateById.getType(),"11"),"只能修改产品合同的子场景");

        //校验是否修改名称，如果修改了名称，判断是否存在该名称的记录
        if (Tools.isNotEmpty(insertVO.getTemplateName()) && !insertVO.getTemplateName().equals(templateById.getTemplateName())) {
            Template templateByName = getTemplateByName(insertVO.getTemplateName());
            AssertUtil.isTrue(templateByName != null && !templateById.getTemplateSn().equals(templateByName.getTemplateSn()), DrawResultCode.TEMPLATE_DUPLICATED);
        }

        Boolean result = transactionTemplate.execute(transactionStatus -> {

            Template template = Template.builder()
                    .templateSn(insertVO.getTemplateId())
                    .templateName(insertVO.getTemplateName())
                    .updatedTime(new Date()).updatedBy(templateById.getCreatedBy())
                    .build();
            templateMapper.updateById(template);

            //批量修改要素值
            List<Element> elements = insertVO.getElements().stream().map(element ->
                    Element.builder().id(element.getEleSn())
                            .selected(element.isSelected() ? 1 : 0)
                            .updatedTime(new Date())
                            .build()).collect(Collectors.toList());
            return this.updateBatchById(elements);
        });

        return result != null && result ? ResponseUtil.success() : ResponseUtil.create(CommonCode.UPDATE_FAIL);
    }

    /**
     * 根据场景名称查询
     * @param templateName 模板名称
     * @return com.turing.draw.model.domain.Template
     * @author lcy
     * @date 2021/6/7 10:25
     **/
    private Template getTemplateByName(String templateName){
        QueryWrapper<Template> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("template_name",templateName);
        return templateMapper.selectOne(queryWrapper);
    }

    @Override public String queryPackageNameById(String packageId){

        if (StringUtils.isEmpty(packageId)) {
            return "";
        }

        if (!StringUtils.isNumeric(packageId)) {
            return "";
        }

        ElementPackage elementPackage = elementPackageMapper.selectById(Integer.valueOf(packageId));

        return Optional.ofNullable(elementPackage).map(ElementPackage::getPackageName).orElse("");

    }

    @Override public String getTemplateTypeBySn(String templateSn){

        Template template = templateMapper.selectById(templateSn);
        return Optional.ofNullable(template).map(t->t.getType().equals("0") ? getTemplateNameById(templateSn) : getTemplateNameById(template.getType())).orElse(null);

    }

    @Override public Integer getEleIdByName(String templateSn,String eleName){

        Element element = new LambdaQueryChainWrapper<>(elementMapper)
                .eq(Element::getTemplateId, templateSn)
                .eq(Element::getName, eleName)
                .last("limit 1")
                .one();

        return Optional.ofNullable(element).map(Element::getId).orElse(null);

    }

    @Override
    public List<String> searchElements(@RequestParam("searchText") String searchText, String templateSn) {
        return new LambdaQueryChainWrapper<>(elementMapper)
                .eq(Element::getTemplateId, templateSn)
                .like(Element::getName, searchText)
                .eq(Element::getIsDel, 0)
                .eq(Element::getSelected, 1)
                .select(Element::getName)
                .list().stream()
                .map(Element::getId)
                .map(x -> x + "") // to string
                .collect(Collectors.toList());
    }

    @Override
    public List<Element> getElementsByTemplateId(String templateId){

        return new LambdaQueryChainWrapper<>(elementMapper).eq(Element::getTemplateId,templateId)
                .eq(Element::getIsDel,0).list();

    }

    @Override
    public List<TemplateVo> getTemplate(Map<String, Integer> privileges) {

        LambdaQueryWrapper<Template> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(Template::getTemplateSn,privileges.keySet());
        queryWrapper.orderByDesc(Template::getCreatedTime);
        List<Template> templates = templateService.list(queryWrapper);
        if(Tools.isNotEmpty(templates)){
            return templates.stream().map(t -> {
                TemplateVo templateVo = new TemplateVo();
                BeanUtils.copyProperties(t, templateVo);
                return templateVo;
            }).collect(Collectors.toList());
        }
        return new ArrayList<>();

    }

    @Override
    public List<TemplateVo> getTemplate() {

        LambdaQueryWrapper<Template> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.orderByDesc(Template::getCreatedTime);
        List<Template> templates = templateService.list(queryWrapper);
        if(Tools.isNotEmpty(templates)){
            return templates.stream().map(t -> {
                TemplateVo templateVo = new TemplateVo();
                BeanUtils.copyProperties(t, templateVo);
                return templateVo;
            }).collect(Collectors.toList());
        }
        return new ArrayList<>();

    }

    @Override
    public List<Element> getElementsByTemplateId(String templateId, Integer category) {

        return new LambdaQueryChainWrapper<>(elementMapper).eq(Element::getTemplateId,templateId)
                .eq(Element::getCategory,category)
                .eq(Element::getIsDel,0).list();

    }

    @Override
    public ElementPageBean<ElementAppVO> queryAppElementByCondition(ElementSearchVO elementSearchVO) {

        Integer pageIndex = elementSearchVO.getPageIndex();
        Integer pageSize = elementSearchVO.getPageSize();
        PageHelper.startPage(pageIndex, pageSize);

        LambdaQueryWrapper<Element> queryWrapper = new LambdaQueryWrapper<>();
        Optional.ofNullable(elementSearchVO.getTemplateId()).filter(StringUtils::isNotEmpty).ifPresent(t -> queryWrapper.eq(Element::getTemplateId, t));
        Optional.ofNullable(elementSearchVO.getSearchText()).filter(StringUtils::isNotEmpty).ifPresent(t -> queryWrapper.like(Element::getName, t));
        Optional.ofNullable(elementSearchVO.getElementName()).filter(StringUtils::isNotEmpty).ifPresent(t -> queryWrapper.like(Element::getName, t));
        queryWrapper.orderByDesc(Element::getCreatedTime);
        queryWrapper.eq(Element::getIsDel, 0);
        queryWrapper.eq(Element::getCategory,1);
        List<Element> elements = baseMapper.selectList(queryWrapper);
        Integer count = baseMapper.selectCount(queryWrapper);
        ElementPageBean<ElementAppVO> pageBean = new ElementPageBean<>(pageIndex,pageSize,count);

        List<ElementAppVO> collect = new ArrayList<>();
        if(Tools.isNotEmpty(elements)){
            collect = elements.stream().map(o -> {
                ElementAppVO elementAppVO = new ElementAppVO();
                elementAppVO.setElementName(o.getName());
                elementAppVO.setId(o.getId());
                return elementAppVO;
            }).collect(Collectors.toList());
        }
        pageBean.setItems(collect);
        return pageBean;
    }

    @Override
    public ElementPageBean<ElementAppVO> queryAllElementByCondition(ElementSearchVO elementSearchVO) {

        String templateId = elementSearchVO.getTemplateId();
        Integer pageIndex = elementSearchVO.getPageIndex();
        Integer pageSize = elementSearchVO.getPageSize();

        List<Integer> elementTickIds = elementSearchVO.getElementTickIds();
        List<Integer> removeElementIds = elementSearchVO.getRemoveElementIds();

        PageHelper.startPage(pageIndex, pageSize);

        LambdaQueryWrapper<Element> queryWrapper = new LambdaQueryWrapper<>();
        Optional.ofNullable(templateId).filter(StringUtils::isNotEmpty).ifPresent(t -> queryWrapper.eq(Element::getTemplateId, t));
        Optional.ofNullable(removeElementIds).filter(Tools::isNotEmpty).ifPresent(t -> queryWrapper.notIn(Element::getId, t));
        Optional.ofNullable(elementTickIds).filter(Tools::isNotEmpty).ifPresent(t -> queryWrapper.in(Element::getId, t));
        queryWrapper.orderByDesc(Element::getCreatedTime);
        queryWrapper.eq(Element::getIsDel, 0);
        queryWrapper.eq(Element::getCategory,1);
        List<Element> elements = baseMapper.selectList(queryWrapper);
        Integer count = baseMapper.selectCount(queryWrapper);

        ElementPageBean<ElementAppVO> pageBean = new ElementPageBean<>(pageIndex, pageSize, count);
        if (CollectionUtils.isEmpty(elements)) {
            return pageBean;
        }
        List<ElementAppVO> elementVOS = typeConversion(elements);
        pageBean.setItems(elementVOS);
        return pageBean;
    }

    private List<ElementAppVO> typeConversion(List<Element> elements){
       return elements.stream().map(t -> {
            ElementAppVO elementVO = new ElementAppVO();
            elementVO.setElementName(t.getName());
            elementVO.setId(t.getId());
            return elementVO;
        }).collect(Collectors.toList());
    }

    @Override
    public List<ElementAppVO> queryAllTemplateElementByConditon(ElementSearchVO elementSearchVO) {

        String templateId = elementSearchVO.getTemplateId();
        List<Integer> elementTickIds = elementSearchVO.getElementTickIds();
        List<Integer> removeElementIds = elementSearchVO.getRemoveElementIds();

        LambdaQueryWrapper<Element> queryWrapper = new LambdaQueryWrapper<>();
        Optional.ofNullable(templateId).filter(StringUtils::isNotEmpty).ifPresent(t -> queryWrapper.eq(Element::getTemplateId, t));
        Optional.ofNullable(removeElementIds).filter(Tools::isNotEmpty).ifPresent(t -> queryWrapper.notIn(Element::getId, t));
        Optional.ofNullable(elementTickIds).filter(Tools::isNotEmpty).ifPresent(t -> queryWrapper.in(Element::getId, t));
        queryWrapper.orderByDesc(Element::getCreatedTime);
        queryWrapper.eq(Element::getIsDel, 0);
        queryWrapper.eq(Element::getCategory,1);
        List<Element> elements = baseMapper.selectList(queryWrapper);

        List<ElementAppVO> elementVOS = new ArrayList<>();
        if(Tools.isNotEmpty(elements)) {
            elementVOS = typeConversion(elements);
        }

        Template template = templateService.getTemplateById(templateId);
        if(template != null) {
            ElementAppVO elementAppVO = new ElementAppVO();
            elementAppVO.setTemplateId(template.getTemplateSn());
            elementAppVO.setTemplateName(template.getTemplateName());
            elementVOS.add(elementAppVO);
        }
        return elementVOS;
    }


    @Override public String requireOrCreateElement(String templateId,String eleName){

        List<Element> elements = new LambdaQueryChainWrapper<>(elementMapper).eq(Element::getTemplateId,templateId)
                .eq(Element::getName,eleName).list();
        if (elements != null && elements.size() > 0) {
            return elements.get(0).getId() + "";
        }

        Element insert = Element.builder().templateId(templateId)
                .type("多项")
                .name(eleName)
                .packageSn("1")
                .selected(1).build();

        elementMapper.insert(insert);
        Integer id = insert.getId();
        Log.info(">>>>>>>> created element <" + eleName + ">, templateId is <" + templateId + ">, id is <" + id + "> <" + insert.getId() + ">");
        if (id > 1) {
            return id + "";
        }
        return insert.getId() + "";

    }



}
